package org.groovyflow.spring.web;

import groovy.lang.Binding;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.groovyflow.file.DifferenceFinder;
import org.groovyflow.file.FileMapMaker;
import org.groovyflow.file.Traverser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;

//TODO  Base this class on AbstractURLHandlerMapping, use a URLPathHelper, and 
//save mappings in a handlerMap, as AbstractURLHandlerMapping does.  (Note that a subclass
//won't have direct access to that cache.) Release the
//handlerMap cache when the Reloader discovers there's been a change under they
//dynamic root.
//!!Better yet, we must be very similar to org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping.
//!!If we don't cache mappings, @SessionAttribute might not work!  At least, performance
//will suffer, won't it?  !!@SessionAttribute does work!!
public class GroovyFlowHandlerMapping extends AbstractHandlerMapping {

	private Thread t;
	private String dynamicRoot;
	private ControllerGroovyScriptEngineManager controllerGroovyScriptEngineManager;
	private ControllerClassNameFinder controllerClassNameFinder = new StandardControllerClassNameFinder();
	private String dynamicScriptName = "dynamicScript.groovy";
	
	public void setControllerGroovyScriptEngineManager(ControllerGroovyScriptEngineManager mgr){
		this.controllerGroovyScriptEngineManager = mgr;
	}
	
	public void setDynamicRoot(String dynamicRoot) {
		this.dynamicRoot = dynamicRoot;
	}
	
	public void setDynamicScriptName(String dynamicScriptName) {
		this.dynamicScriptName = dynamicScriptName;
	}	
	
	@Autowired
	public void setControllerClassNameFinder(
			ControllerClassNameFinder controllerClassNameFinder) {
		this.controllerClassNameFinder = controllerClassNameFinder;
	}	
	

	@Override
	protected Object getHandlerInternal(HttpServletRequest req)
			throws Exception {
		Object handler = null;
		Exception exception;
		
		Binding binding = new Binding();
		binding.setVariable("className", this.controllerClassNameFinder.findClassName(req));
		
		this.controllerGroovyScriptEngineManager.run(dynamicScriptName, binding);  
		handler =  binding.getVariable("handler");
		exception = (Exception)  binding.getVariable("exception");
			
		if(exception != null && !exception.toString().contains("java.io.FileNotFoundException"))
					throw exception;
		
		//handler could be null
		return handler;

	}
	
	public void init(){
		controllerGroovyScriptEngineManager = new ControllerGroovyScriptEngineManager();
		controllerGroovyScriptEngineManager.setMode("dev");
		//Isn't there some singleton list I'm forgetting that would cut out two lines of code?
		ArrayList list = new ArrayList();
		list.add(this.dynamicRoot);
		controllerGroovyScriptEngineManager.setDynamicRoots(list);
		controllerGroovyScriptEngineManager.initGSE();
		//TODO  Probably someone isn't using this HandlerMapping if we're not in dev.
		//Either that, or we should subclass some other HandlerMapping and hand off to them
		//when we're not in dev.
	 	t = new Thread(new Reloader());
		t.setDaemon(true);
		t.start();
		
	}
	
	
	private Object lock = new Object();
	private class Reloader implements Runnable{
		Traverser trav;
		Map fileMap;
		public void run() {
			while(true){
				//For now assume there's only one dynamic root
				if(trav == null){
					File root = new File((String) controllerGroovyScriptEngineManager.getDynamicRoots().get(0));
					trav = new Traverser(root);
				}	
				if(fileMap == null){
					FileMapMaker mapMaker = new FileMapMaker();
					trav.setFileHandler(mapMaker);
					trav.traverse();
					this.fileMap = mapMaker.getFileMap();
				}
				else{
					DifferenceFinder diffFinder = new DifferenceFinder();
					diffFinder.setMapFile(fileMap);
					trav.setFileHandler(diffFinder);
					trav.traverse();
					if(diffFinder.isChanged()){
						trav = null;
						fileMap = null;
						//In the future might have caches to clear.
						/*getInterceptorCache().clear();
						if(sqlDao != null)
							sqlDao.getTableCache().clear();*/
						controllerGroovyScriptEngineManager.initGSE();
					}
				}
				
				try {
					Thread.sleep(4000);
				} catch (InterruptedException e) {
				}			
			}
		}
	}
}
